/*
 * 描          述:  <描述>
 * 修  改   人:  PengQingyang
 * 修改时间:  2022年11月11日
 * <修改描述:>
 */
package com.tx.component.attachment.service;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.map.HashedMap;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import com.tx.component.attachment.args.AttachmentDetailAddArgs;
import com.tx.component.attachment.args.AttachmentDetailDeleteArgs;
import com.tx.component.attachment.args.AttachmentDetailQueryArgs;
import com.tx.component.attachment.args.AttachmentDetailSaveArgs;
import com.tx.component.attachment.model.AttachmentDetail;
import com.tx.component.attachment.model.AttachmentInstance;
import com.tx.component.attachment.model.AttachmentTag;
import com.tx.component.file.catalog.VitualFileCatalog;
import com.tx.component.file.context.FileContext;
import com.tx.component.file.exception.FileContextResourceException;
import com.tx.core.exceptions.util.AssertUtils;
import com.tx.core.paged.model.PagedList;
import com.tx.core.util.MessageUtils;

/**
 * 附件详情业务层<br/>
 * <功能详细描述>
 * 
 * @author  PengQingyang
 * @version  [版本号, 2022年11月11日]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class AttachmentDetailService {
    
    /** 附件实例业务层 */
    private AttachmentInstanceService attachmentInstanceService;
    
    /** <默认构造函数> */
    public AttachmentDetailService() {
        super();
    }
    
    /** <默认构造函数> */
    public AttachmentDetailService(
            AttachmentInstanceService attachmentInstanceService) {
        super();
        this.attachmentInstanceService = attachmentInstanceService;
    }
    
    /**
     * 删除文件<br/>
     * <功能详细描述>
     * @param attachmentId [参数说明]
     * 
     * @return void [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public void delete(AttachmentDetailDeleteArgs deleteArgs) {
        AssertUtils.notNull(deleteArgs, "deleteArgs is null.");
        
        if (deleteArgs.isRecyclable()) {//可回收的处理逻辑
            //如果附件为可回收
            if (StringUtils.hasLength(deleteArgs.getAttachmentId())) {
                //将数据移动到历史表
                this.attachmentInstanceService
                        .moveToHisById(deleteArgs.getAttachmentId());
            } else if (StringUtils.hasLength(deleteArgs.getRelativePath())) {
                //如果为根据相对路径删除，则先查询实例再进行处理
                AttachmentInstance attach = this.attachmentInstanceService
                        .findByRelativePath(deleteArgs.getRelativePath(),
                                deleteArgs.getCatalog());
                if (attach != null) {
                    this.attachmentInstanceService
                            .moveToHisById(attach.getId());
                }
            } else {
                if (!CollectionUtils.isEmpty(deleteArgs.getAttachmentIds())) {
                    //批量移动到历史表
                    deleteArgs.getAttachmentIds()
                            .stream()
                            .forEach(idTemp -> {
                                //如果不属于前两种，则匹配该方法
                                this.attachmentInstanceService
                                        .moveToHisById(idTemp);
                            });
                }
            }
        } else {
            //如果附件为不可回收的处理逻辑
            if (StringUtils.hasLength(deleteArgs.getAttachmentId())) {
                //查询实例
                AttachmentInstance att = this.attachmentInstanceService
                        .findById(deleteArgs.getAttachmentId());
                if (att != null) {
                    AttachmentDetail attDetail = new AttachmentDetail(att);
                    //删除数据
                    this.attachmentInstanceService
                            .deleteById(deleteArgs.getAttachmentId());
                    if (attDetail.getResource() != null) {
                        //删除对应的文件
                        attDetail.getResource().delete();
                    }
                }
            } else if (StringUtils.hasLength(deleteArgs.getRelativePath())) {
                //如果为根据相对路径删除，则先查询实例再进行处理
                AttachmentInstance att = this.attachmentInstanceService
                        .findByRelativePath(deleteArgs.getRelativePath(),
                                deleteArgs.getCatalog());
                if (att != null) {
                    AttachmentDetail attDetail = new AttachmentDetail(att);
                    //删除数据
                    this.attachmentInstanceService
                            .deleteById(deleteArgs.getAttachmentId());
                    if (attDetail.getResource() != null) {
                        //删除对应的文件
                        attDetail.getResource().delete();
                    }
                }
            } else {
                if (!CollectionUtils.isEmpty(deleteArgs.getAttachmentIds())) {
                    //批量移动到历史表
                    deleteArgs.getAttachmentIds().stream().forEach(idTemp -> {
                        AttachmentInstance att = this.attachmentInstanceService
                                .findById(idTemp);
                        if (att != null) {
                            AttachmentDetail attDetail = new AttachmentDetail(
                                    att);
                            //删除数据
                            this.attachmentInstanceService.deleteById(idTemp);
                            if (attDetail.getResource() != null) {
                                //删除对应的文件
                                attDetail.getResource().delete();
                            }
                        }
                    });
                }
            }
        }
        
    }
    
    /**
     * 查询附件详情<br/>
     * <功能详细描述>
     * @param attachmentId
     * @return [参数说明]
     * 
     * @return AttachmentDetail [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public AttachmentDetail findById(String attachmentId) {
        AssertUtils.notEmpty(attachmentId, "attachmentId is empty.");
        
        AttachmentInstance attIns = this.attachmentInstanceService
                .findById(attachmentId);
        if (attIns == null) {
            return null;
        }
        AttachmentDetail attDetail = new AttachmentDetail(attIns);
        return attDetail;
    }
    
    /**
     * 根据附件ID查询附件详情<br/>
     * <功能详细描述>
     * @param attachmentId
     * @return [参数说明]
     * 
     * @return AttachmentDetail [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public AttachmentDetail findByRelativePath(String relativePath,
            String catalog) {
        AssertUtils.notEmpty(relativePath, "relativePath is empty.");
        AssertUtils.notEmpty(catalog, "catalog is empty.");
        
        AttachmentInstance attIns = this.attachmentInstanceService
                .findByRelativePath(relativePath, catalog);
        if (attIns == null) {
            return null;
        }
        AttachmentDetail attDetail = new AttachmentDetail(attIns);
        return attDetail;
    }
    
    /**
     * 统计符合条件的附件数量<br/>
     * <功能详细描述>
     * @param queryArgs
     * @return [参数说明]
     * 
     * @return int [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public int count(AttachmentDetailQueryArgs queryArgs) {
        Map<String, Object> params = toQueryParams(queryArgs);
        
        int c = this.attachmentInstanceService.count(params);
        return c;
    }
    
    /**
     * 查询附件清单<br/>
     * <功能详细描述>
     * @param queryArgs
     * @return [参数说明]
     * 
     * @return List<AttachmentDetail> [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public List<AttachmentDetail> list(AttachmentDetailQueryArgs queryArgs) {
        Map<String, Object> params = toQueryParams(queryArgs);
        
        List<AttachmentDetail> resList = this.attachmentInstanceService
                .queryList(params)
                .stream()
                .map(i -> {
                    return new AttachmentDetail(i);
                })
                .collect(Collectors.toList());
        return resList;
    }
    
    /**
     * 分页查询附件清单<br/>
     * <功能详细描述>
     * @param queryArgs
     * @param pageIndex
     * @param pageSize
     * @return [参数说明]
     * 
     * @return PagedList<AttachmentDetail> [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    public PagedList<AttachmentDetail> pages(
            AttachmentDetailQueryArgs queryArgs, int pageIndex, int pageSize) {
        Map<String, Object> params = toQueryParams(queryArgs);
        
        PagedList<AttachmentInstance> pages = this.attachmentInstanceService
                .queryPagedList(params, pageIndex, pageSize);
        PagedList<AttachmentDetail> res = new PagedList<>();
        res.setCount(pages.getCount());
        res.setPageIndex(pages.getPageIndex());
        res.setPageSize(pages.getPageSize());
        res.setList(pages.getList()
                .stream()
                .map(i -> new AttachmentDetail(i))
                .collect(Collectors.toList()));
        return res;
    }
    
    /**
     * 将查询参数转化为查询Map<String,Object>实例
     * <功能详细描述>
     * @param queryArgs
     * @return [参数说明]
     * 
     * @return Map<String,Object> [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    private Map<String, Object> toQueryParams(
            AttachmentDetailQueryArgs queryArgs) {
        AssertUtils.notNull(queryArgs, "queryArgs is null.");
        BeanWrapper bw = PropertyAccessorFactory
                .forBeanPropertyAccess(queryArgs);
        Map<String, Object> params = new HashedMap<>();
        for (PropertyDescriptor pd : bw.getPropertyDescriptors()) {
            if (pd.getReadMethod() != null && pd.getWriteMethod() != null) {
                String name = pd.getName();
                Object obj = bw.getPropertyValue(pd.getName());
                if (obj != null) {
                    if (obj instanceof String) {
                        params.put(name, obj);
                    } else if (obj instanceof String[]) {
                        params.put(name, obj);
                    } else if (obj instanceof List) {
                        params.put(name, obj);
                    }
                }
            }
        }
        //单独处理两个Map
        if (MapUtils.isNotEmpty(queryArgs.getTagValueEquals())) {
            Map<String, String> tagValueEquals = new LinkedHashMap<>(
                    queryArgs.getTagValueEquals()
                            .entrySet()
                            .stream()
                            .filter(et -> StringUtils.hasLength(et.getKey())
                                    && StringUtils.hasLength(et.getValue()))
                            .collect(Collectors.toMap(et -> et.getKey(),
                                    et -> et.getValue())));
            if (MapUtils.isNotEmpty(tagValueEquals)) {
                params.put("tagValueEquals", tagValueEquals);
            }
        }
        if (MapUtils.isNotEmpty(queryArgs.getTagValueIns())) {
            MultiValueMap<String, String> tagValueIns = new LinkedMultiValueMap<>();
            queryArgs.getTagValueIns()
                    .entrySet()
                    .stream()
                    .filter(et -> StringUtils.hasLength(et.getKey())
                            && CollectionUtils.isNotEmpty(et.getValue()))
                    .forEach(et -> {
                        tagValueIns.put(et.getKey(), et.getValue());
                    });
            if (MapUtils.isNotEmpty(tagValueIns)) {
                params.put("tagValueIns", tagValueIns);
            }
        }
        return params;
    }
    
    /**
     * 保存文件<br/>
     * 如果文件已经存在，则复写当前文件<br/>
     * 如果文件不存在，则创建文件后写入<br/>
     * 如果对应文件所在的文件夹不存在，对应文件夹会自动创建<br/>
     *
     * @param catalog 文件存储目录
     * @param relativePath 存储路径,此存储路径为文件全路径(包括扩展名)
     * @param input        文件流
     * 
     * @return FileDefinitionDetail 文件定义的实体,以及文件对应访问地址,权限等
     * @throws throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public AttachmentDetail save(String catalog,
            AttachmentDetailSaveArgs saveArgs) {
        AssertUtils.notEmpty(catalog, "catalog is empty.");
        AssertUtils.notNull(saveArgs, "saveArgs is null.");
        
        AssertUtils.isTrue(
                saveArgs.getMultipartFile() != null
                        || saveArgs.getInputStream() != null,
                "saveArgs.input is null.");
        AssertUtils.notEmpty(saveArgs.getRelativePath(),
                "saveArgs.relativePath is empty.");
        
        VitualFileCatalog vfc = FileContext.getEngine()
                .getVitualFileCatalog(catalog);
        AssertUtils.notNull(vfc, "vfc is null.catalog:{}", catalog);
        
        AttachmentInstance attachment = new AttachmentInstance();
        attachment.setCatalog(catalog);
        attachment.setRelativePath(saveArgs.getRelativePath());
        attachment.setOriginalFilename(saveArgs.getOriginalFilename());
        attachment.setCategoryKey(saveArgs.getCategoryKey());
        attachment.setBusinessKey(saveArgs.getBusinessKey());
        attachment.setTypeKey(saveArgs.getTypeKey());
        if (saveArgs.getMultipartFile() != null) {
            FileContext.getEngine()
                    .save(vfc,
                            saveArgs.getRelativePath(),
                            saveArgs.getMultipartFile());
            attachment.setSize(saveArgs.getMultipartFile().getSize());
        } else if (saveArgs.getInputStream() != null) {
            FileContext.getEngine()
                    .save(vfc,
                            saveArgs.getRelativePath(),
                            saveArgs.getInputStream());
            try {
                attachment.setSize(saveArgs.getInputStream().available());
            } catch (IOException e) {
                throw new FileContextResourceException(MessageUtils
                        .format("save inputstream.available() error."), e);
            }
        }
        
        //处理tag
        if (!MapUtils.isEmpty(saveArgs.getTags())) {
            attachment.setTags(saveArgs.getTags()
                    .entrySet()
                    .stream()
                    .filter(et -> StringUtils.hasLength(et.getKey())
                            && StringUtils.hasLength(et.getValue()))
                    .map(et -> {
                        AttachmentTag tag = new AttachmentTag();
                        tag.setName(et.getKey());
                        tag.setText(et.getValue());
                        return tag;
                    })
                    .collect(Collectors.toList()));
        }
        //保存实例
        this.attachmentInstanceService.save(attachment);
        
        AttachmentDetail res = new AttachmentDetail(attachment);
        return res;
    }
    
    /**
     * 新增文件<br/>
     * 如果文件已经存在，则抛出异常<br/>
     * 如果文件不存在，则创建文件后写入<br/>
     * 如果对应文件所在的文件夹不存在，对应文件夹会自动创建<br/>
     *
     * @param catalog 文件存储目录
     * @param relativePath 存储路径,此存储路径为文件全路径(包括扩展名)
     * @param input        文件流
     * 
     * @return FileDefinitionDetail 文件定义的实体,以及文件对应访问地址,权限等
     * @throws IOException 
     * @throws throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
     */
    @Transactional
    public AttachmentDetail add(String catalog,
            AttachmentDetailAddArgs addArgs) {
        AssertUtils.notEmpty(catalog, "catalog is empty.");
        AssertUtils.notNull(addArgs, "addArgs is null.");
        
        AssertUtils.isTrue(
                addArgs.getMultipartFile() != null
                        || addArgs.getInputStream() != null,
                "addArgs.input is null.");
        AssertUtils.notEmpty(addArgs.getRelativePath(),
                "addArgs.relativePath is empty.");
        
        VitualFileCatalog vfc = FileContext.getEngine()
                .getVitualFileCatalog(catalog);
        AssertUtils.notNull(vfc, "vfc is null.catalog:{}", catalog);
        
        AttachmentInstance attachment = new AttachmentInstance();
        attachment.setCatalog(catalog);
        attachment.setRelativePath(addArgs.getRelativePath());
        attachment.setOriginalFilename(addArgs.getOriginalFilename());
        attachment.setCategoryKey(addArgs.getCategoryKey());
        attachment.setBusinessKey(addArgs.getBusinessKey());
        attachment.setTypeKey(addArgs.getTypeKey());
        if (addArgs.getMultipartFile() != null) {
            FileContext.getEngine()
                    .add(vfc,
                            addArgs.getRelativePath(),
                            addArgs.getMultipartFile());
            attachment.setSize(addArgs.getMultipartFile().getSize());
        } else if (addArgs.getInputStream() != null) {
            FileContext.getEngine()
                    .add(vfc,
                            addArgs.getRelativePath(),
                            addArgs.getInputStream());
            try {
                attachment.setSize(addArgs.getInputStream().available());
            } catch (IOException e) {
                throw new FileContextResourceException(MessageUtils
                        .format("add inputstream.available() error."), e);
            }
        }
        
        //处理tag
        if (!MapUtils.isEmpty(addArgs.getTags())) {
            attachment.setTags(addArgs.getTags()
                    .entrySet()
                    .stream()
                    .filter(et -> StringUtils.hasLength(et.getKey())
                            && StringUtils.hasLength(et.getValue()))
                    .map(et -> {
                        AttachmentTag tag = new AttachmentTag();
                        tag.setName(et.getKey());
                        tag.setText(et.getValue());
                        return tag;
                    })
                    .collect(Collectors.toList()));
        }
        //保存实例
        this.attachmentInstanceService.insert(attachment);
        
        AttachmentDetail res = new AttachmentDetail(attachment);
        return res;
    }
}
